﻿using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using Furion;
using Furion.Authorization;
using Furion.DataEncryption;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using SqlSugar;
using PMBF.Core.Enums;
using PMBF.Core.Plugins;
using DB.Core.Models;
using PMBF.Core.ViewModels;

namespace PMBF.Application.Api
{
    /// <summary>
    /// 登录服务
    /// </summary>
    [AppAuthorize, ApiDescriptionSettings("认证与安全管理@10")]
    public class AuthenticationService : IDynamicApiController
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ISqlSugarRepository repository;
        private readonly SqlSugarClient db;

        /// <summary>
        /// LoginApiService
        /// </summary>
        /// <param name="httpContextAccessor"></param>
        /// <param name="sqlSugarRepository"></param>
        public AuthenticationService(IHttpContextAccessor httpContextAccessor, ISqlSugarRepository sqlSugarRepository)
        {
            _httpContextAccessor = httpContextAccessor;
            repository = sqlSugarRepository;
            db = repository.Context;
        }

        /// <summary>
        /// 登录认证
        /// </summary>
        /// <param name="loginForm"></param>
        /// <returns></returns>
        [AllowAnonymous]
        public object Login(LoginResult loginForm)
        {

            User _user = db.Queryable<User>().Where(p => p.UserCode == loginForm.Usercode && p.Password == StringHelper.GeneratePassword(loginForm.Password)).First();

            //更新登录日志
            var _ipHelper = new IPHelper(_httpContextAccessor);

            //获取数据库时间
            var _dbTime = db.Ado.GetString("select getdate()");

            object rs;

            Log _log = new()
            {
                OperateIP = _ipHelper.GetIP() + "-" + _ipHelper.GetLocation(_ipHelper.GetIP()).ToString(),

                ClientInfo = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].FirstOrDefault().ToString(),

                LogType = "登录",

                RequestContent = "尝试登录",

                OperateTime = Convert.ToDateTime(_dbTime), //DateTime.Now,

                ClientType = loginForm.ClientType,
            };

            if (_user is null)
            {
                rs = new { Success = false, message = "登录失败，请重新输入正常的用户名、密码！" };

                _log.UserID = 0;

                _log.UserCode = loginForm.Usercode;

                _log.UserName = "未知访客！！！";

                _log.RequestResult = "登录失败";

                db.Insertable(_log).ExecuteCommand();

                //此处不能抛出网络异常，通过业务异常处理
                //throw Oops.Oh(SystemErrorCodes.E0001);
            }
            else
            {
                #region token

                // 生成 token
                var jwtSettings = App.Configuration.GetSection("JWTSettings").Get<JWTSettingsOptions>();

                var datetimeOffset = DateTimeOffset.UtcNow;

                string _token = JWTEncryption.Encrypt(jwtSettings.IssuerSigningKey, new Dictionary<string, object>()
                {
                    // 存储Id
                    { "UserId", _user.ID },
                    // 存储用户名
                    { "LoginCode",_user.UserCode },
                    // 存储角色ID
                    { "RoleId", "暂空"},
                    // IP地址
                    { "UserIP", ""},
                    // 客户端类型
                    { "AppType", Guid.NewGuid().ToString()}, 
                    //iat: jwt的签发时间
                    { JwtRegisteredClaimNames.Iat, datetimeOffset.ToUnixTimeSeconds() },
                    //nbf: 定义在什么时间之前，该jwt都是不可用的.
                    { JwtRegisteredClaimNames.Nbf, datetimeOffset.ToUnixTimeSeconds() },
                    //exp: jwt的过期时间，这个过期时间必须要大于签发时间
                    { JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddSeconds(jwtSettings.ExpiredTime.Value * 60).ToUnixTimeSeconds() },
                    //iss: jwt签发者
                    { JwtRegisteredClaimNames.Iss, jwtSettings.ValidIssuer},
                    //aud: 接收jwt的一方 jwtSettings.ValidAudience 接收方改变是否token通用？
                    { JwtRegisteredClaimNames.Aud, jwtSettings.ValidAudience } //Guid.NewGuid().ToString()
            });

                // 设置 Swagger 刷新自动授权
                _httpContextAccessor.SigninToSwagger(_token);

                // 获取刷新 token
                var refreshToken = JWTEncryption.GenerateRefreshToken(_token, 7);

                //_user.Password = "";
                //_user.TokenName = _token;
                //_user.RefreshTokenName = refreshToken;
                // 设置请求报文头
                _httpContextAccessor.HttpContext.Response.Headers["access-token"] = _token;
                _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

                #endregion

                rs = new { Success = true, token = _token, message = "登录成功！", userId = _user.ID };

                _log.UserID = _user.ID;

                _log.UserCode = _user.UserCode;

                _log.UserName = _user.UserName;

                _log.RequestResult = "登录成功！！！";

                db.Insertable(_log).ExecuteCommand();
            }

            return rs;
        }

        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public User GetCurrentUser(int userId)
        {
            User _user = db.Queryable<User>().Where(p => p.ID == userId).First();

            if (_user == null) throw Oops.Oh(SystemErrorCodes.E0003);

            //待处理图片服务器返回image url
            _user.Avatar = App.Configuration["ImageServiceSettings:api"] + "/" + _user.Avatar;

            //获取用户权限
            var _access = db.Queryable<User>("o")
                 .AddJoinInfo("UserGroup", "g", "o.ID= g.UserID", JoinType.Inner)
                 .AddJoinInfo("GroupMenu", "gm", "g.GroupID = gm.GroupID", JoinType.Inner)
                 .AddJoinInfo("menuRule", "m", "gm.MenuRuleID = m.ID ", JoinType.Inner)
                 .Where(o => o.ID == userId)
                 .Select<string>("m.name")
                 .ToList();

            _user.Access = _access;

            return _user;
        }

        public List<MenuRule> GetCurrentUserMenu(int userId)
        {
            var _userMenu = new List<MenuRule>();

            _userMenu = db.Queryable<User, UserGroup, GroupMenu, MenuRule>(
                (u,ug,gm, mr)=>  new JoinQueryInfos(
                    JoinType.Inner, u.ID == ug.UserID,
                    JoinType.Inner, ug.GroupID == gm.GroupID,
                    JoinType.Inner, gm.MenuRuleID == mr.ID))
                  .Where((u, ug, gm, mr) => u.ID == userId)
                  .Select((u, ug, gm, mr) => mr).MergeTable()
                  .OrderBy(mr => mr.ID)
                  .OrderBy(mr => mr.ParentMenuRuleID)
                  .ToTree(mr => mr.Routes, mr => mr.ParentMenuRuleID, 0);

            return _userMenu;
        }

        /// <summary>
        /// 注销Swagger
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public object Logout()
        {
            _httpContextAccessor.SignoutToSwagger();

            return null;
        }

    }
}
